# Created by yang
from django.db.models.fields.related import RelatedField, ForeignKey, ManyToManyField
from django.shortcuts import HttpResponse, render, redirect
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.http import QueryDict
from django.forms import ModelForm
from types import FunctionType


from utils.page import Pagination
import copy


class FilterOption(object):
    def __init__(self, field_or_func, is_multi=False, text_func_name=None, val_func_name=None):
        """
        :param field: 字段名称或函数
        :param is_multi: 是否支持多选
        :param text_func_name: 在Model中定义函数，显示文本名称，默认使用 str(对象)
        :param val_func_name:  在Model中定义函数，显示文本名称，默认使用 对象.pk
        """
        self.field_or_func = field_or_func
        self.is_multi = is_multi
        self.text_func_name = text_func_name
        self.val_func_name = val_func_name

    @property
    def is_func(self):
        if isinstance(self.field_or_func, FunctionType):
            return True

    @property
    def name(self):
        if self.is_func:
            return self.field_or_func.__name__
        else:
            return self.field_or_func

class RowItems(object):
    def xxx(self,url_dict,pk):
        """从url_dict里面删除所有的pk,为了实现点击取消选择"""
        l = list(set(url_dict))
        l.remove(pk)
        return l

    def __init__(self, option, change_list,data_list, request):

        self.option = option
        self.data_list = data_list
        self.change_list = change_list
        self.param_dict = copy.deepcopy(request.GET)
        self.param_dict._mutable=True

    def __iter__(self):

        base_url = self.change_list.model_config.changelist_url
        tpl = "<a href='{0}' class='{1}'>{2}</a>"
        # 全部
        if self.option.name in self.param_dict:
            pop_value = self.param_dict.pop(self.option.name)
            url = "{0}?{1}".format(base_url, self.param_dict.urlencode())
            val = tpl.format(url, '', '全部')
            self.param_dict.setlist(self.option.name, pop_value)
        else:
            url = "{0}?{1}".format(base_url, self.param_dict.urlencode())
            val = tpl.format(url, 'active', '全部')

        # self.param_dict

        yield mark_safe("<div class='whole'>")
        yield mark_safe(val)
        yield mark_safe("</div>")

        yield mark_safe("<div class='others'>")
        for obj in self.data_list:

            param_dict = copy.deepcopy(self.param_dict)

            # url上要传递的值
            pk = self.option.val_func_name(obj) if self.option.val_func_name else obj.pk
            pk = str(pk)

            # a标签上显示的内容
            text = self.option.text_func_name(obj) if self.option.text_func_name else str(obj)

            exist = False
            if pk in param_dict.getlist(self.option.name):
                exist = True

            if self.option.is_multi:
                if exist:
                    values = param_dict.getlist(self.option.name)
                    values.remove(pk)
                    param_dict.setlist(self.option.name, values)
                else:
                    param_dict.appendlist(self.option.name, pk)
            else:
                param_dict[self.option.name] = pk
            url = "{0}?{1}".format(base_url, param_dict.urlencode())
            val = tpl.format(url, 'active' if exist else '', text)
            yield mark_safe(val)
        yield mark_safe("</div>")


    # def __iter__(self):
    #     """注释掉先,已实现,"""
    #     current_pk_list=self.param.getlist(self.option.name)
    #
    #     url_dict = self.param  #group=5&group=6&roles=3
    #     url_dict._mutable = True
    #
    #     if url_dict.get(self.option.name):
    #         url_dict.pop(self.option.name)
    #     tag = '<a href="?%s">全部:</a>' % url_dict.urlencode()
    #     yield mark_safe(tag)
    #     for model_obj in self.data_list:
    #         #将当前obj的pk传入_url_dict以便每个a标签用来生成url
    #         tmp=[]
    #         tmp.extend(current_pk_list)
    #         if self.option.is_multi:
    #
    #             tmp.append(str(model_obj.pk))
    #             url_dict.setlist(self.option.name,tmp)
    #         else:
    #             url_dict[self.option.name] = str(model_obj.pk)
    #         if str(model_obj.pk) in current_pk_list:
    #             #_url_dict中存在两个相同的pk说明该pk已经被选中,此时点击需要将它删除,以便在url上也删除
    #             url_dict.setlist(self.option.name, self.xxx(tmp,str(model_obj.pk)))
    #             tag = '<a href="?{}" class="active">{}</a>'.format(url_dict.urlencode(), str(model_obj))
    #         else:
    #             tag = '<a href="?{}">{}</a>'.format(url_dict.urlencode(), str(model_obj))
    #         yield mark_safe(tag)


class ChangeList(object):
    def __init__(self, data_list, model_config):
        self.model_config = model_config
        self.list_display = model_config.get_list_display()
        self.actions = model_config.get_actions()
        self.list_filter = model_config.get_list_filter()

        request_get = copy.deepcopy(model_config.request.GET)
        request_get._mutable = True

        page = Pagination(
            current_page=model_config.request.GET.get("page", 1),
            total_item_count=data_list.count(),
            base_url=model_config.request.path_info,
            per_page_count=2,
            request_param=request_get
        )

        self.data_list = data_list[page.start:page.end]
        self.page_html = page.page_html()

    def get_add_btn(self):
        query_dict = self.model_config.request.GET
        query_dict._mutable = True
        query_dict["_xxxxxxxxxxxxxxxxxxxx"] = query_dict.urlencode()

        base_url = self.model_config.get_base_url("add")

        add_btn = '<a href="%s?%s" class="btn btn-primary">添加</a>' % (base_url, query_dict.urlencode())

        return mark_safe(add_btn)

    def gen_list_filter(self):

        for option in self.model_config.list_filter:

            if option.is_func:
                data_list = option.field_or_func(self.model_config, self, option)
            else:
                _field = self.model_config.model_class._meta.get_field(option.field_or_func)

                if isinstance(_field, ForeignKey):
                    data_list = RowItems(option, self, _field.rel.model.objects.all(), self.model_config.request)
                elif isinstance(_field, ManyToManyField):
                    data_list = RowItems(option, self, _field.rel.model.objects.all(), self.model_config.request)
                else:
                    data_list = RowItems(option, self, _field.model.objects.all(), self.model_config.request)
            yield data_list


class ModelNb(object):
    list_display = []
    actions = []

    def __init__(self, model_class, site):
        self.model_class = model_class
        self.site = site
        self.app_label = model_class._meta.app_label
        self.model_name = model_class._meta.model_name

    show_add_btn = True

    def get_show_add_btn(self):
        return self.show_add_btn

    def check(self, obj=None, isheader=False):
        if isheader:
            return '选择'
        else:
            tpl = '<input type="checkbox" name="pk" value="%s">' % obj.pk
            return mark_safe(tpl)

    def option(self, obj=None, isheader=False):
        if isheader:
            return '操作'
        else:
            query_dict = self.request.GET
            if "_xxxxxxxxxxxxxxxxxxxx" not in query_dict:
                query_dict._mutable = True
                query_dict["_xxxxxxxxxxxxxxxxxxxx"] = query_dict.urlencode()
            base_url_chang = self.get_base_url(type="change", pk=obj.pk)
            base_url_del = self.get_base_url(type="delete", pk=obj.pk)

            edit_url = base_url_chang + "?" + query_dict.urlencode()
            del_url = base_url_del + "?" + query_dict.urlencode()

            tpl = '<a href="%s">编辑</a>|<a href="%s">删除</a>' % (edit_url, del_url)
            return mark_safe(tpl)

    def multi_del(self):
        pk_list = self.request.POST.getlist("pk")
        print("pk_list:", pk_list)
        # self.model_class.objects.filter(pk__in=pk_list).delete()

    multi_del.short_desc = "批量删除"

    def init_action(self):
        pass

    init_action.short_desc = "初始化"

    def get_list_display(self):
        result = []
        if self.list_display:
            result.extend(self.list_display)
            result.insert(0, ModelNb.check)
            result.append(ModelNb.option)
        return result

    def get_actions(self):
        result = []
        result.extend(self.actions)
        result.append(ModelNb.init_action)
        result.append(ModelNb.multi_del)
        return result

    def get_base_url(self, type="changelist", pk=None):
        """根据类型反向获取base_url"""

        params = self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name, type

        if pk:
            return reverse("%s:%s_%s_%s" % params, args=[pk])
        return reverse("%s:%s_%s_%s" % params)

    model_form = None

    def get_form_class(self):
        result = self.model_form
        if not result:
            class DefaultModelForm(ModelForm):
                class Meta():
                    model = self.model_class
                    fields = "__all__"

            result = DefaultModelForm
        return result

    ####定制组合筛选
    list_filter = []

    def get_list_filter(self):
        return self.list_filter

    @property
    def changelist_url(self):
        base_url = reverse("{0}:{1}_{2}_changelist".format(self.site.namespace, self.app_label, self.model_name))
        return base_url

    @property
    def urls(self):
        return self.get_urls(), None, None

    def changelist_view(self, request, *args, **kwargs):
        self.request = request

        if request.method == "POST":
            action_name = request.POST.get("action", None)
            if action_name:
                action_method = getattr(self, action_name)
                action_method()

        data_list = self.model_class.objects.all()

        cl = ChangeList(data_list, self)

        context = {
            "cl": cl,
        }

        return render(request, "niubin/changelist.html", context)

    def add_view(self, request, *args, **kwargs):
        self.request = request
        if request.method == "GET":
            form = self.get_form_class()()
            context = {
                "form": form
            }
            return render(request, "niubin/add.html", context)

        elif request.method == "POST":
            # 获取到是否是来自popup的提交
            field_id = request.GET.get("popup")

            form = self.get_form_class()(request.POST)
            if form.is_valid():
                obj = form.save()
                if field_id:
                    context = {"obj": obj, "field_id": field_id}
                    return render(request, "niubin/tmp1.html", context)
                # 返回changelist
                param = request.GET.get("_xxxxxxxxxxxxxxxxxxxx")
                base_url = self.get_base_url()
                return redirect("%s?%s" % (base_url, param))
            else:
                context = {
                    "form": form
                }
                return render(request, "niubin/add.html", context)

    def delete_view(self, request, pk, *args, **kwargs):
        self.model_class.objects.filter(pk=pk).delete()
        base_url = self.get_base_url()
        param = request.GET.get("_xxxxxxxxxxxxxxxxxxxx")
        url = base_url + "?" + param
        return redirect(url)

    def change_view(self, request, pk, *args, **kwargs):
        self.request = request
        obj = self.model_class.objects.filter(pk=pk).first()

        if request.method == "GET":
            form = self.get_form_class()(instance=obj)
            context = {
                "form": form
            }
            return render(request, "niubin/change.html", context)
        elif request.method == "POST":
            form = self.get_form_class()(request.POST, instance=obj)
            if form.is_valid():
                form.save()
                param = request.GET.get("_xxxxxxxxxxxxxxxxxxxx")
                base_url = self.get_base_url()
                url = base_url + "?" + param
                return redirect(url)

    def get_urls(self):
        from django.conf.urls import url
        app_label_name = self.model_class._meta.app_label, self.model_class._meta.model_name
        partterns = [
            url(r'^$', self.changelist_view, name="%s_%s_changelist" % app_label_name),
            url(r'^add/$', self.add_view, name="%s_%s_add" % app_label_name),
            url(r'^(.+)/delete/$', self.delete_view, name="%s_%s_delete" % app_label_name),
            url(r'^(.+)/change/$', self.change_view, name="%s_%s_change" % app_label_name),
        ]

        partterns += self.extra_urls()

        return partterns

    def extra_urls(self):
        """扩展url预留的钩子"""
        return []


class NbSite(object):
    def __init__(self):
        self.name = 'nb'
        self.namespace = 'nb'
        self._registry = {}

    def register(self, modle, model_nb=None):
        if not model_nb:
            model_nb = ModelNb
        self._registry[modle] = model_nb(modle, self)

    def login(self, request, *args, **kwargs):

        return HttpResponse("login")

    def logout(self, request, *args, **kwargs):

        return HttpResponse("logout")

    @property
    def urls(self):
        return self.get_urls(), self.name, self.namespace

    def get_urls(self):
        patterns = []

        from django.conf.urls import url

        patterns += [
            url(r"^login/", self.login),
            url(r"^logout/", self.logout),
        ]

        for model_class, model_nb_obj in self._registry.items():
            patterns += [
                url(r"^%s/%s/" % (model_class._meta.app_label, model_class._meta.model_name), model_nb_obj.urls),
            ]

        return patterns


site = NbSite()
